using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using Microsoft.TeamFoundation.Migration.Toolkit;
using Microsoft.TeamFoundation.Migration.Toolkit.Wit;
using Microsoft.TeamFoundation.Migration.Toolkit.VC;
using Microsoft.Vsts.Ranger.Migration.TfsToTfs.Service.Schemas;
using Microsoft.Vsts.Ranger.Migration.TfsToTfs.Service.Data;
using Microsoft.Vsts.Ranger.Migration.TfsToTfs.Service.Common;
using Microsoft.Vsts.Rangers.Migration;

namespace Microsoft.Vsts.Ranger.Migration.TfsToTfs.Service.Business
{
    public class SessionEventManager
    {
        
        private SessionsDataset.EventsDataTable _eventsTable = new SessionsDataset.EventsDataTable();
        private EventData _eventData = new EventData();
        private int _sessionInstanceId = 0;
        private IMigrationSession _session;
        private Hashtable _eventsToExclude = new Hashtable();

        public int SessionInstanceId
        {
            get { return _sessionInstanceId; }
            set {_sessionInstanceId = value; }
        }


        public void AttachEventHandlers(IMigrationSession session)
        {
            _session = session;

            #region Add session handlers...

            session.SessionStart += new EventHandler<MigrationSessionEventArgs>(session_SessionStart);
            session.SessionAborted += new EventHandler<MigrationSessionEventArgs>(session_SessionAborted);
            session.SessionComplete += new EventHandler<MigrationSessionEventArgs>(session_SessionComplete);
            session.SessionError += new EventHandler<MigrationSessionEventArgs>(session_SessionError);
            session.SessionStart +=new EventHandler<MigrationSessionEventArgs>(session_SessionStart);
            
            
            #endregion

            if (session is Microsoft.Vsts.Rangers.Migration.TfsToTfs.Session)
            {
                Microsoft.Vsts.Rangers.Migration.TfsToTfs.Session tfsToTfsSession = (Microsoft.Vsts.Rangers.Migration.TfsToTfs.Session)session;
            }
            if (session is VersionControlSession)
            {
                VersionControlSession vcSession = (VersionControlSession)session;

                #region Add vc session handlers...
                vcSession.AnalysisAborted += new EventHandler<VersionControlEventArgs>(vcSession_AnalysisAborted);
                vcSession.AnalysisComplete += new EventHandler<VersionControlEventArgs>(vcSession_AnalysisComplete);
                vcSession.AnalysisError += new EventHandler<VersionControlEventArgs>(vcSession_AnalysisError);
                vcSession.AnalysisStarting += new EventHandler<VersionControlEventArgs>(vcSession_AnalysisStarting);
                vcSession.AnalyzingChangeComplete += new EventHandler<VersionControlEventArgs>(vcSession_AnalyzingChangeComplete);
                vcSession.AnalyzingChangeStarting += new EventHandler<VersionControlEventArgs>(vcSession_AnalyzingChangeStarting);
                vcSession.MigratingChangeComplete += new EventHandler<VersionControlEventArgs>(vcSession_MigratingChangeComplete);
                vcSession.MigratingChangeStarting += new EventHandler<VersionControlEventArgs>(vcSession_MigratingChangeStarting);
                vcSession.MigrationAborted += new EventHandler<VersionControlEventArgs>(vcSession_MigrationAborted);
                vcSession.MigrationComplete += new EventHandler<VersionControlEventArgs>(vcSession_MigrationComplete);
                vcSession.MigrationError += new EventHandler<VersionControlEventArgs>(vcSession_MigrationError);
                vcSession.MigrationStarting += new EventHandler<VersionControlEventArgs>(vcSession_MigrationStarting);
                

                #endregion

                #region Load up events to exclude

                _eventsToExclude.Clear();

                string[] eventsList = System.Configuration.ConfigurationManager.AppSettings["EventsToExclude"].Split(";".ToCharArray());
                foreach (string item in eventsList)
                    _eventsToExclude.Add(item, item);

                #endregion
            }


            if (session is WorkItemTrackingSession)
            {
                WorkItemTrackingSession witSession = (WorkItemTrackingSession)session;

                #region Add wit session handlers...

                witSession.MissingUser +=new EventHandler<MissingUserEventArgs>(witSession_MissingUser);
                witSession.WorkItemFieldConflict +=new EventHandler<FieldConflictEventArgs>(witSession_WorkItemFieldConflict);
                witSession.WorkItemMigrationComplete +=new EventHandler<MigrationCompleteEventArgs>(witSession_WorkItemMigrationComplete);
                witSession.WorkItemAnalysisComplete += new EventHandler<AnalysisCompleteEventArgs>(witSession_WorkItemAnalysisComplete);
                witSession.WorkItemMigrationStart += new EventHandler<WorkItemMigrationEventArgs>(witSession_WorkItemMigrationStart);
                witSession.WorkItemMigrationFailed += new EventHandler<WorkItemMigrationEventArgs>(witSession_WorkItemMigrationFailed);
                witSession.WorkItemLinksOutOfSync += new EventHandler<CollectionEventArgs>(witSession_WorkItemLinksOutOfSync);
                witSession.WorkItemAttachmentsOutOfSync += new EventHandler<CollectionEventArgs>(witSession_WorkItemAttachmentsOutOfSync);
                #endregion
            }

        }




        public void StoreSessionInstance(bool oneTimeOnly, bool oneWayMigration)
        {

            _sessionInstanceId = _eventData.InsertSessionInstanceRow(_session.Id, oneTimeOnly, oneWayMigration);
        }

        public void UpdateSessionInstance(string currentStatus)
        {
            _eventData.UpdateSessionInstance(_sessionInstanceId, currentStatus);
        }

  
        #region Event handlers

        void tfsToTfsSession_MigrateSingleItemError(object sender, BatchedItemEventArgs e)
        {
            LogEvent(MigrationEvents.MigrateSingleItemError, e);
        }

        void tfsToTfsSession_MigrateSingleItemWarning(object sender, BatchedItemEventArgs e)
        {
            LogEvent(MigrationEvents.MigrateSingleItemWarning, e);
        }


        void vcSession_AnalysisComplete(object sender, MigrationEventArgs e)
        {            
            LogEvent(MigrationEvents.AnalysisComplete, e);
        }

        void session_SessionStart(object sender, MigrationSessionEventArgs e)
        {
            LogEvent(MigrationEvents.SessionStart, e);
        }

        void session_SessionError(object sender, MigrationSessionEventArgs e)
        {
            LogEvent(MigrationEvents.SessionError, e);
        }

        void session_SessionComplete(object sender, MigrationSessionEventArgs e)
        {
            LogEvent(MigrationEvents.SessionComplete, e);
        }

        void session_SessionAborted(object sender, MigrationSessionEventArgs e)
        {
            LogEvent(MigrationEvents.SessionAborted, e);
        }

        void witSession_WorkItemMigrationStart(object sender, WorkItemMigrationEventArgs e)
        {
            LogEvent(MigrationEvents.WorkItemMigrationStart, e);
        }

        void witSession_WorkItemMigrationFailed(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.WorkItemMigrationFailed, e);
        }

        void witSession_WorkItemMigrationComplete(object sender, MigrationCompleteEventArgs e)
        {

            LogEvent(MigrationEvents.WorkItemMigrationComplete, e);
        }

        // This appears to be deprecated.
        //void witSession_WorkItemLinksConflict(object sender, LinkConflictEventArgs e)
        //{
        //    LogEvent("WorkItemLinksConflict", e);
        //}

        void witSession_WorkItemFieldConflict(object sender, FieldConflictEventArgs e)
        {
            LogEvent(MigrationEvents.WorkItemFieldConflict, e);
        }

        // This appears to be deprecated.
        //void witSession_WorkItemAttachmentsConflict(object sender, AttachmentConflictEventArgs e)
        //{
        //    LogEvent("WorkItemAttachmentsConflict", e);
        //}

        void witSession_MissingUser(object sender, MissingUserEventArgs e)
        {
            LogEvent(MigrationEvents.MissingUser, e);
        }

        void witSession_WorkItemMigrationFailed(object sender, WorkItemMigrationEventArgs e)
        {
            LogEvent(MigrationEvents.WorkItemMigrationFailed, e);
        }

        void witSession_WorkItemAnalysisComplete(object sender, AnalysisCompleteEventArgs e)
        {
            LogEvent(MigrationEvents.WorkItemAnalysisComplete, e);
        }

        void witSession_WorkItemAttachmentsOutOfSync(object sender, CollectionEventArgs e)
        {
            LogEvent(MigrationEvents.WorkItemAttachmentsOutOfSync, e);
        }

        void witSession_WorkItemLinksOutOfSync(object sender, CollectionEventArgs e)
        {
            LogEvent(MigrationEvents.WorkItemLinksOutOfSync, e);
        }


        void vcSession_MigrationStarting(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.MigrationStarting, e);
        }

        void vcSession_MigrationResumed(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.MigrationResumed, e);
        }

        void vcSession_MigrationPaused(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.MigrationPaused, e);
        }

        void vcSession_MigrationError(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.MigrationError, e);
        }

        void vcSession_MigrationComplete(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.MigrationComplete, e);
        }

        void vcSession_MigrationAborted(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.MigrationAborted, e);
        }

        void vcSession_MigratingChangeStarting(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.MigratingChangeStarted, e);
        }

        void vcSession_MigratingChangeComplete(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.MigratingChangeComplete, e);
        }

        void vcSession_AnalyzingChangeStarting(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.AnalyzingChangeStarted, e);
        }

        void vcSession_AnalyzingChangeComplete(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.AnalyzingChangeComplete, e);
        }

        void vcSession_AnalysisStarting(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.AnalysisStarting, e);
        }

        void vcSession_AnalysisResumed(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.AnalysisResumed, e);
        }

        void vcSession_AnalysisPaused(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.AnalysisPaused, e);
        }

        void vcSession_AnalysisError(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.AnalysisError, e);
        }

        void vcSession_AnalysisAborted(object sender, MigrationEventArgs e)
        {
            LogEvent(MigrationEvents.AnalysisAborted, e);
        }

# endregion



        #region Logevent Methods

        private void LogEvent(MigrationEvents migrationEventType, BatchedItemEventArgs e)
        {
            try
            {

                // Check to see if this event is excluded from being logged
                if (IsEventExcluded(migrationEventType))
                    return;

                SessionsDataset.EventsRow row = _eventData.GetNewRow();

                //Get the migration type
                row.EventType = migrationEventType.ToString();

                if (_sessionInstanceId > 0)
                {
                    //Get the ID of the session instance to correlate to the Session
                    row.SessionInstanceID = _sessionInstanceId;
                }
                else
                {
                    System.Threading.Thread.Sleep(2000);

                    if (_sessionInstanceId > 0)
                        row.SessionInstanceID = _sessionInstanceId;
                }
                row.Description = e.Message;

                row.Time = DateTime.Now;

                if (e.Exception != null)
                    row.Exception = e.Exception.ToString();

                //Store the row so that it can be batched to the database
                if (row.SessionInstanceID > 0)
                {
                    _eventData.StoreEventRow(row);
                }
                else
                {
                    LogNullSessionInstanceIdForEventsRow(row);
                }
            }
            catch (Exception ex)
            {
                //If an exception is caught here, just log it to the event log since errors shouldn't
                //be caught here (this is just logging events that occur).  
                ExceptionManager.WriteEventLogEntry("An error occurred trying to store an event to the database.", ex);
            }

            
        }

        private static void LogNullSessionInstanceIdForEventsRow(SessionsDataset.EventsRow row)
        {
            StringBuilder strBuilder = new StringBuilder();
            strBuilder.AppendLine("An error occurred trying to store an event to the database. SessionInstanceId is null for the event:");
            strBuilder.AppendLine(string.Format("    EventType: {0}", row.EventType));
            strBuilder.AppendLine(string.Format("    Description: {0}", string.IsNullOrEmpty(row.Description) ? "n/a" : row.Description));
            strBuilder.AppendLine(string.Format("    Time: {0:yyyy/MM/dd HH:mm:ss.fff}", row.Time));
            strBuilder.AppendLine(string.Format("    Exception: {0}", string.IsNullOrEmpty(row.Exception) ? "n/a" : row.Exception));
            System.Diagnostics.Trace.Write(strBuilder.ToString());
        }


        private void LogEvent(MigrationEvents migrationEventType, MigrationEventArgs e)
        {
            try
            {

                // Check to see if this event is excluded from being logged
                if (IsEventExcluded(migrationEventType))
                    return;

                SessionsDataset.EventsRow row = _eventData.GetNewRow();

                //Get the migration type
                row.EventType = migrationEventType.ToString();

                //Get the ID of the session instance to correlate to the Session
                row.SessionInstanceID = _sessionInstanceId;

                //Get the common fields from the MigrationEventArg base Event type
                GetCommonFields(row, e);

                //Get the fields for each of the Event Types
                GetEventTypeSpecificFields(migrationEventType, e, row);

                //Apply specific logic for each of the event types
                switch (migrationEventType)
                {
                    case MigrationEvents.SessionStart:
                        break;
                    case MigrationEvents.SessionComplete:
                        UpdateSessionInstance(SessionInstanceStatuses.Completed.ToString());
                        break;

                    default:
                        break;
                }                

                //Store the row so that it can be batched to the database
                //In some cases, MigrationComplete event, which is not bound to a session,
                //is fired when workspace is cleaned. _sessionInstanceId is 0 in this case,
                //and the UI will neglect the event
                if (row.SessionInstanceID > 0)
                {
                    _eventData.StoreEventRow(row);
                }
                else
                {
                    LogNullSessionInstanceIdForEventsRow(row);
                }
            }
            catch (Exception ex)
            {
                //If an exception is caught here, just log it to the event log since errors shouldn't
                //be caught here (this is just logging events that occur).  
                ExceptionManager.WriteEventLogEntry("An error occurred trying to store an event to the database.", ex);
            }
        }

        private void GetCommonFields(SessionsDataset.EventsRow row, MigrationEventArgs e)
        {
            row.Description = e.Description;

            if (e.Exception != null)
                row.Exception = e.Exception.ToString();

            row.Time = e.Time;

            
        }

        private void GetEventTypeSpecificFields(MigrationEvents migrationEventType, MigrationEventArgs e, SessionsDataset.EventsRow row)
        {
            if (e is VersionControlEventArgs)
                GetTypeSpecificFields(row, (VersionControlEventArgs)e);

            if (e is WorkItemMigrationEventArgs)
                GetTypeSpecificFields(row, (WorkItemMigrationEventArgs)e);

            if (e is MigrationSessionEventArgs)
                GetTypeSpecificFields(row, (MigrationSessionEventArgs)e);

            if (e is MigrationCompleteEventArgs)
                GetTypeSpecificFields(row, (MigrationCompleteEventArgs)e);
            
            if (e is AnalysisCompleteEventArgs)
                GetTypeSpecificFields(row, (AnalysisCompleteEventArgs)e);
            
            if (e is FieldConflictEventArgs)
                GetTypeSpecificFields(row, (FieldConflictEventArgs)e);
            
            if (e is CollectionEventArgs)
                GetTypeSpecificFields(row, (CollectionEventArgs)e);

            if (e is MissingUserEventArgs)
                GetTypeSpecificFields(row, (MissingUserEventArgs)e);
        }


        private void GetTypeSpecificFields(SessionsDataset.EventsRow row, VersionControlEventArgs e)
        {
            //TODO:  Figure out what to log from VersionControlEvents
            if(e.ChangeGroup != null)
                row.Ids = e.ChangeGroup.ChangeGroupId.ToString();
        }


        private void GetTypeSpecificFields(SessionsDataset.EventsRow row, MigrationSessionEventArgs e)
        {
            row.MigratedFailureCount = e.FailedItemCount;
            row.MigratedItemCount = e.MigratedItemCount;
            row.Description = e.Description;
        }

        private void GetTypeSpecificFields(SessionsDataset.EventsRow row, WorkItemMigrationEventArgs e)
        {
            row.SourceID = (e.SourceId == null ? null : e.SourceId.Id);
            row.Description = e.Description;            
        }
        private void GetTypeSpecificFields(SessionsDataset.EventsRow row, MigrationCompleteEventArgs e)
        {
            if (e != null)
            {
                //Get the overloaded desc if it exists
                row.Description = e.Description;

                row.SourceRevisions = e.SourceRevisions;
                row.TargetRevisions = e.TargetRevisions;
                row.SourceID = (e.SourceId == null ? null : e.SourceId.Id);
                row.TargetID = (e.TargetId == null ? null : e.TargetId.Id);
            }
        }

        private void GetTypeSpecificFields(SessionsDataset.EventsRow row, AnalysisCompleteEventArgs e)
        {
            //Get the overloaded desc if it exists
            row.Description = e.Description;

            row.SourceRevisions = e.SourceRevisions;
            row.TargetRevisions = e.TargetRevisions;
            row.SourceID = (e.SourceId == null ? null : e.SourceId.Id);
            row.TargetID = (e.TargetId == null ? null : e.TargetId.Id);
        }
        
        private void GetTypeSpecificFields(SessionsDataset.EventsRow row, FieldConflictEventArgs e)
        {
           
            row.FieldName = e.FieldName;

            //Overloaded description has all of the information for this event
            row.Description = e.Description;

        }
        private void GetTypeSpecificFields(SessionsDataset.EventsRow row, CollectionEventArgs e)
        {
            row.SourceID = (e.SourceId == null ? null : e.SourceId.Id);
            row.TargetID = (e.TargetId == null ? null : e.TargetId.Id);

            row.Description = e.Description;
        }
        private void GetTypeSpecificFields(SessionsDataset.EventsRow row, MissingUserEventArgs e)
        {          
            row.Description = e.Description;
            row.Username = e.UserName;
            row.WorkItemStore = e.WorkItemStore.StoreName;
        }

        # endregion

        private bool IsEventExcluded(MigrationEvents migrationEventType)
        {
            return _eventsToExclude.ContainsKey(migrationEventType.ToString());
        }



    }


}
